package org.phalanx.messenging.management;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;

import org.phalanx.messenging.AbstractMessage;
import org.phalanx.messenging.MessageSendable;
import org.phalanx.messenging.Messageable;

public class MessageManager{
	private Hashtable<Messageable,ArrayList<MessageListener>> messageListeners;
	
	public MessageManager(){
		messageListeners = new Hashtable<Messageable,ArrayList<MessageListener>>();
	}
	
	public void addMessageListener(Messageable m, MessageListener msgEventClass){
		ArrayList<MessageListener> newListenerList = new ArrayList<MessageListener>();
		
		if (!messageListeners.containsKey(m)){
			newListenerList.add(msgEventClass);
		}
		else {
			newListenerList = messageListeners.get(m);
			newListenerList.add(msgEventClass);
		}
		
		messageListeners.put(m, newListenerList);
	}
	
	public void removeMessageListener(Messageable m, MessageListener msgEventClass){
		if (messageListeners.containsKey(m)){
			ArrayList<MessageListener> currentListenerList = messageListeners.get(m);
			
			if (!currentListenerList.isEmpty()){
				currentListenerList.remove(msgEventClass);
			}
		}
	}
	
	public void recieveMessage(String newMessage, String sendingUser){
		// Converts serialized string message to a Messageable
		PhalanxMetaMessage pmMsg = new PhalanxMetaMessage();
		pmMsg.setUserName(sendingUser);
		
		AbstractMessage msg = new AbstractMessage();
		msg.setMessageHeader(parseMessageHeader(newMessage));
		populateMessageContents(msg, newMessage);
		
		onIncomingMessage(msg, pmMsg);
	}
	
	private String parseMessageHeader(String newMessage){
		String messageHeader = "";
		
		String[] token = messageHeader.split(":");
		if (token.length > 1){
			messageHeader = token[0];
		}
		
		return messageHeader;
	}
	
	private void populateMessageContents(AbstractMessage aMsg, String newMessage){
		String contentsText = newMessage.substring(0,newMessage.indexOf(":"));
		
		if (!contentsText.isEmpty()){
			String[] arrayOfContents = newMessage.substring(newMessage.indexOf(":") + 1).split(",");
			
			if (arrayOfContents.length > 0){
				for (int i = 0; i < arrayOfContents.length; i++){
					String[] keyValue = arrayOfContents[i].split("=");
					
					if (keyValue.length > 0){
						aMsg.setToMessage(keyValue[0], keyValue[1]);
					}
				}
			}
		}
	}
	
	private void onIncomingMessage(Messageable m, PhalanxMetaMessage pmMsg){
		// This method grabs the processed messages that are incoming
		// and delegates them to the right class. When the right class is 
		// found based on the message type it then sends it to a group of classes
		// that are listening to the message.
		
		Enumeration<Messageable> e = messageListeners.keys();
		Messageable listedMessage = null;
		Messageable refMsg = null;
		boolean foundMatch = false;
		
		while (e.hasMoreElements() && !foundMatch){
			Messageable referenceMessage = e.nextElement();
			
			if (m.getMessageHeader().equals(referenceMessage.getMessageHeader())){
				refMsg = referenceMessage;
				listedMessage = createNewMessageType(referenceMessage);
				listedMessage.applyStringToMessage(m.generateMessageBody());
				foundMatch = true;
			}
		}
		
		if (listedMessage != null){
			ArrayList<MessageListener> eventList = messageListeners.get(refMsg);
			
			// In this section we want to invoke the MessageListener's class of
			// processMessage(messageClassObject, PhalanxMetaMessage)
			// so that the listener classes don't have to worry about casting types
			// for this we use reflection
			for (int i = 0; i < eventList.size(); i++){
				MessageListener currentMsgListener = eventList.get(i);
				try {
					Method processMessageMethod = currentMsgListener.getClass().
					getMethod(MessageListener.PROCESS_MESSAGE, listedMessage.getClass(), pmMsg.getClass());
					Object[] param =  {listedMessage, pmMsg}; 
					processMessageMethod.invoke(currentMsgListener, param);
				} catch (Exception broken){
					broken.printStackTrace();
				}
			}
		}
	}
	
	private Messageable createNewMessageType(Messageable m){
		Object newMsgObject = null;
		try {
		
			Class newInstanceClass = Class.forName(m.getClass().getName());
			newMsgObject = newInstanceClass.newInstance();
		
		} catch (ClassNotFoundException cnfe){}
		catch (IllegalAccessException iae){}
		catch (InstantiationException ie){}
		
		Messageable messageableObject = (Messageable)newMsgObject;
		return messageableObject;
	}
	
	public void sendMessage(MessageSendable send, Messageable msg){
		send.sendMessage(msg);
	}	
}
